﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;

namespace System.Runtime.Serialization
{
  public class Package
  {
    private static Dictionary<Type, ISerializationFactory> _factories = new Dictionary<Type, ISerializationFactory>();
    public static void RegisterAssembly(Assembly asm)
    {
      foreach (Type t in asm.GetTypes().Where(x => x.HasCustomAttribute<CerealizableAttribute>()))
        RegisterType(t);
    }

    public static void RegisterType(Type t)
    {
      if (!t.HasCustomAttribute<CerealizableAttribute>())
        throw new Exception("Type '{0}' does not have a serializable attribute.");
      _factories[t] = t.GetCustomAttribute<CerealizableAttribute>().Factory;
    }

    private MemoryStream _buffer;

    public int DataVersion { get; }
    public string Format { get; }
    public IFormatter Formatter { get; private set; }
    public StreamOptimization DesiredOptimization { get; private set; }
    public object DisposableContext { get; set; }
    public ISerializable Context { get; set; }


    public Package(Stream destination, StreamOptimization optimization)
    {
      _buffer = new MemoryStream(2048);
      DesiredOptimization = optimization;
     

      //switch (optimization == StreamOptimization.Default ? StreamOptimization.Performance : optimization)
      //{
      //  case StreamOptimization.Performance:
      //    Formatter = new BinaryFormatter();
      //    break;
      //  case StreamOptimization.Size:
      //    Formatter = new BinaryFormatter();
      //    break;
      //  case StreamOptimization.HumanReadable:
      //    Formatter = new Xml.Serialization.formatter
      //    break;
      //  default:
      //    throw new Exception(String.Format("Unknown optimization: '{0}'", optimization));
      //}
    }

    public void Add<T>(T obj)
    {
      Add(obj, typeof(T));
    }

    public void Add(object obj, Type type)
    {
      var factory = _factories[type];
      if (factory == null)
        throw new Exception(String.Format("Type '{0}' not registered for serialization", typeof(T));
      Add(obj, factory);
    }

    public void Add(object obj, ISerializationFactory factory)
    {
      factory.Serialize(this, obj);
    }

    public T Get<T>() 
    {
      return (T)Get(typeof(T));
    }

    public object Get(Type t)
    {
      var factory = _factories[t];
      if (factory == null)
        throw new Exception(String.Format("Type '{0}' not registered for serialization", t));
      return Get(factory);
    }

    public object Get(ISerializationFactory factory)
    {
      return factory.Deserialize(this);
    }

    public IEnumerable<T> Get<T>(int count) 
    {
      for (int i = 0; i < count; i++)
        yield return Get<T>();
    }
  }
}
